package net.qiqbframework.messaging.annotation;


import net.qiqbframework.common.Priority;
import net.qiqbframework.messaging.InterceptorChain;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.task.CurrentMessageTask;

import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.concurrent.Callable;

@Priority(Priority.HIGHEST)
public class InterceptorChainParameterResolverFactory implements HandlerParameterResolverFactory, HandlerParameterResolver<InterceptorChain> {

    private static final String INTERCEPTOR_CHAIN_EMITTER_KEY = InterceptorChain.class.getName();

    private static final ThreadLocal<InterceptorChain> CURRENT = new ThreadLocal<>();

    public static <R> R callWithInterceptorChain(InterceptorChain interceptorChain, Callable<R> action) throws Exception {
        InterceptorChain previous = CURRENT.get();
        CURRENT.set(interceptorChain);
        try {
            return action.call();
        } finally {
            if (previous == null) {
                CURRENT.remove();
            } else {
                CURRENT.set(previous);
            }
        }
    }

    /**
     * Returns the current interceptor chain registered for injection as a parameter. Will return
     * the instance passed in {@link #callWithInterceptorChain(InterceptorChain, Callable)}. When invoked outside the
     * scope of that method, this will return {@code null}.
     *
     * @return the InterceptorChain instance passed in {@link #callWithInterceptorChain(InterceptorChain, Callable)}
     */
    public static InterceptorChain currentInterceptorChain() {
        return CURRENT.get();
    }

    @Override
    public InterceptorChain resolveParameter(Message<?> message) {
        InterceptorChain interceptorChain = CURRENT.get();
        if (interceptorChain != null) {
            return interceptorChain;
        }
        return CurrentMessageTask.map(uow -> (InterceptorChain) uow.getResource(INTERCEPTOR_CHAIN_EMITTER_KEY))
                .orElseThrow(() -> new IllegalStateException(
                        "InterceptorChain should have been injected"));
    }

    @Override
    public boolean support(Message<?> message) {
        return CURRENT.get() != null || CurrentMessageTask.isStarted() && CurrentMessageTask.get().resources().containsKey(INTERCEPTOR_CHAIN_EMITTER_KEY);
    }

    @Override
    public HandlerParameterResolver<InterceptorChain> createInstance(Executable executable, Parameter[] parameters, int parameterIndex) {
        if (InterceptorChain.class.equals(parameters[parameterIndex].getType())) {
            return this;
        }
        return null;
    }
}
